# coding: utf-8
import os
import sys
import logging
import pygame
import re
import imp

if (hasattr(sys, "frozen") # new py2exe
        or hasattr(sys, "importers") # old py2exe
        or imp.is_frozen("__main__")):
    rootdir = os.path.dirname(sys.executable)
else:
    rootdir = os.path.dirname(sys.argv[0])

try:
    __file__    # note py2exe can have __file__. refer into the `.exe` file
except NameError:
    insertdir = rootdir
else:
    insertdir = os.path.dirname(os.path.dirname(__file__))

logger = logging.getLogger('wordson.topic')
logger.debug('rootdir=%s; insertdir=%s', rootdir, insertdir)

sys.path.insert(0, insertdir)
from lib.bashlog import getlogger
from lib.tracemore import get_exc_plus
from lib.minsix import Str
from lib.config import Config
from lib.circle import Circle
from lib.inputer import Inputer
from lib.wrap import Wrapper
from lib.sprite import Sprite
from lib.sprite import Group
from lib import events
from lib import color
sys.path.pop(0)

cfg = Config()

class Text(Sprite):
    '''The topic user need to anwser'''
    def __init__(self, width=None, text='', line_interval=0):
        super(Text, self).__init__()

        self._width = 70 if width is None else width
        self.wrapper = Wrapper(width=self._width)
        self.text = text
        self.line_interval = line_interval

        self.font = cfg.font

        self.image = self.fontsuf()
        self.rect = self.image.get_rect()

    def fontsuf(self, text=None, color=color.black):
        if text is None:
            text = self.text
        textline = self.wrapper.wrap(text).splitlines()
        textsurf = list(map(lambda t: self.font.render(t, True, color, None), textline))

        max_height = 0
        max_width = 0
        for each in textsurf:
            rect = each.get_rect()
            max_height = max(max_height, rect.height)
            max_width = max(max_width, rect.width)

        surf_height = max((max_height+self.line_interval) * len(textsurf) - self.line_interval, 0)
        surf_width = max_width

        surf = pygame.Surface((surf_width, surf_height), flags=pygame.SRCALPHA)
        start = 0
        while textsurf:
            font_suf = textsurf.pop(0)
            surf.blit(font_suf, (0, start))
            start += (max_height + self.line_interval)
        return surf

    # @property
    # def font(self):    # lazy load. Cannot use before init pygame
    #     return cfg.font

    @property
    def width(self):
        return self._width
    @width.setter
    def width(self, value):
        self._width = value
        self.wrapper = value

class Title(Text):
    def __init__(self, text=''):
        width = cfg.topic_len
        hspace = cfg.topic_hspace
        super(Title, self).__init__(width, text, line_interval=hspace)
        self.rect.x = cfg.topic_x
        self.rect.y = cfg.topic_y
        self.font = cfg.new_font(40)
        self.wrapper.width = 30

    def set(self, text):
        self.text = text
        topleft = self.rect.topleft
        self.image = self.fontsuf()
        self.rect = self.image.get_rect()
        self.rect.topleft = topleft

        logger.debug('%s at %s', text, self.rect)


class Option(Sprite):
    def __init__(self, text=''):
        self._rawtext = text
        self.text = Text(cfg.option_len, text)
        super(Option, self).__init__()
        self.circle = Circle()
        self._checked = False
        rectt = self.text.rect
        rectc = self.circle.rect

        # rectc.x = x
        # rectc.y = y
        rectt.left = rectc.right + 3
        rectt.top = rectc.top

        self.rect = pygame.Rect(0, 0, 0, 0)
        self._update()

        self.refresh = False
        self.bind_key = None

    @property
    def checked(self):
        return self._checked
    @checked.setter
    def checked(self, value):
        self.circle.DRAW = value
        self._checked = value


    @property
    def rawtext(self):
        return self._rawtext
    @rawtext.setter
    def rawtext(self, value):
        self._rawtext = value
        # save old rect position
        oldrect = self.text.rect
        left = oldrect.left
        top = oldrect.top
        # refresh the font image
        self.text = Text(cfg.option_len, value)
        self.text.image = self.text.fontsuf()
        # restore rect position
        rect = self.text.image.get_rect()
        rect.left = left
        rect.top = top
        self.text.rect = rect
        # self.refresh = True
        self._update()

    def handle(self, event):
        if self.text.handle(event):
            logger.debug('%s handled', event)
            return True
        if self.circle.handle(event):
            logger.debug('%s handled by circle', event)
            return True
        if (event.type == pygame.MOUSEMOTION
                and not self.checked):
            pos = event.pos
            collided = self.rect.collidepoint(pos)
            if collided != self.circle.DRAW:
                logger.debug('collided %s, %s', collided, self)
                self.circle.DRAW = collided
        elif event.type == pygame.KEYUP:
            if event.key == self.bind_key:
                logger.info('key currect %s', event.key)
                self.checked = True
            else:
                self.checked = False


    def collidepoint(self, pos):
        for rect in (self.circle.rect, self.text.rect):
            if rect.collidepoint(pos):
                return True
        return False

    def update(self, time):
        if not self.circle.DONE:
            self.refresh = True    # the circle is drawing. Need calc image
        self.text.update(time)
        self.circle.update(time)

        if self.refresh:
            self._update()

    def _update(self):
        '''join the image together'''
        t = self.text
        c = self.circle
        rt = t.rect
        rc = c.rect

        width = rt.width + rc.width
        height = max(rt.height, rc.height)

        self.rect.size = (width, height)
        img = pygame.Surface((width, height), flags=pygame.SRCALPHA)
        img.blit(c.image, (0, 0))
        img.blit(t.image, (rc.width, 0))
        self.image = img



    def draw(self, surface):
        self.text.draw(surface)
        self.circle.draw(surface)

    def __str__(self):
        result = super(Option, self).__str__()
        return ''.join((result[:-1], 'bind_key=', pygame.key.name(self.bind_key), '>'))


class Topic(Group):
    SELECT = True
    auto_1st_char = True
    def __init__(self, auto_1st_char=True, top=100):
        super(Topic, self).__init__()
        self.auto_1st_char = auto_1st_char
        t = Title()
        t.rect.top = top
        self.title = t
        self.options = []
        for key in (pygame.K_a, pygame.K_b, pygame.K_c, pygame.K_d):
            opt = Option()
            opt.bind_key = key
            self.options.append(opt)
        self.inputer = Inputer()
        self.add(self.title)
        self.refresh_sprites()

    @property
    def bottom(self):
        if self.SELECT:
            return self.options[-1].rect.bottom
        return self.inputer.rect.bottom

    def handle(self, event):
        result = False
        # select
        if self.SELECT:
            post = None
            if (event.type == pygame.MOUSEBUTTONUP
                    and event.button == 1):

                for each in self.options:
                    if each.rect.collidepoint(event.pos):
                        logger.info('anwser=%s', each.bind_key)
                        each.checked = True

                        post = each.bind_key
                    else:
                        each.checked = False
            elif event.type == pygame.KEYUP:
                key = event.key
                for each in self.options:
                    if each.bind_key == key:
                        each.checked = True
                        post = key
                        break

            if post is not None:
                pygame.event.post(events.answer(post))
                result = True
        # input
        elif (not self.SELECT
                and event.type == pygame.KEYUP
                and event.key == pygame.K_RETURN):
            result = self.inputer.value().strip()
            if result:
                logger.info('anwser=%s', result)
                pygame.event.post(events.answer(result))
            else:
                logger.debug('empty anwser')
            return True


        if not result:
            return super(Topic, self).handle(event)
        return result


    def set(self, title, options=None, placeholder=None):
        if placeholder is not None:
            self.inputer.placeholder = placeholder
        # else:
            # self.inputer.placeholder = cfg.placeholder
        select = (options is not None)
        if select != self.SELECT:
            logger.debug('set to select: %s', select)
            self.SELECT = select
            self.refresh_sprites()

        self.title.set(title)
        if select:
            for text, opt, s in zip(options, self.options, ('A. ', 'B. ', 'C. ', 'D.')):
                if self.auto_1st_char:
                    text = s + text
                opt.rawtext = text
                opt.checked = False

        self.inputer.clear()

        self.rearange()

    def refresh_sprites(self):
        logger.debug('refresh sprites: select=%s', self.SELECT)
        if self.SELECT:
            kill = (self.inputer, )
            add = self.options
        else:
            kill = self.options
            add = (self.inputer, )

        for each in kill:
            each.kill()

        for each in add:
            self.add(each)



    def rearange(self):
        '''adjust the rect of each sprite'''
        gap = 20

        rectt = self.title.rect
        top = rectt.bottom + gap
        left = rectt.left
        centerx = rectt.centerx


        recti = self.inputer.rect
        # recti.centerx = centerx
        recti.top = top

        for each in self.options:
            recto = each.rect
            recto.left = left
            recto.top = top
            top += recto.height

    def update(self, time):
        # if self.title.update(time):
        #     return True
        # if any(map(lambda x: x.update(time), self.options)):
        #     return True
        # if self.inputer.update(time):
        #     return True
        self.title.update(time)
        self.inputer.update(time)
        for each in self.options:
            each.update(time)


if __name__ == '__main__':
    getlogger(logger, logging.DEBUG)

    pygame.init()
    pygame.key.set_repeat(cfg.key_repeat_delay, cfg.key_repeat_interval)
    screen = pygame.display.set_mode(cfg.screen)


    run = True
    clock = pygame.time.Clock()
    # title = Title("""I'm using python with pygame and trying to get the width of text. The pygame documentation says to use pygame.font.Font.size(). 但我有点不明白应该使用哪个函数。我一直收到`TypeError: descriptor 'size' requires a 'pygame.font.Font' object but received a 'str'`的错误。""")
    # title.set("Test")
    # title.image = title.fontsuf()
    # opt = Option(0, 0, 'Test')
    # cir = opt.circle
    topic = Topic()
    topic.set("Test title", "Test Inputer", False)
    topic.set("I'm using python with pygame a。",
              "重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启重启;"
              "关机关机关机关机关机关机关机关机关机关机关机;还原;换用Ubuntu系统啊Ubuntu啊这年头谁还用Windows写代码啊".split(';'),
              True)
                # "...",
                # False)

    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
            # opt.handle(event)
            topic.handle(event)

        inter = clock.tick(cfg.tick)
        # opt.update(inter)
        topic.update(inter)
        screen.fill(color.white)
        # title.draw(screen)
        # opt.draw(screen)
        topic.draw(screen)
        # topic.title.draw(screen)
        pygame.display.flip()

    pygame.quit()
